﻿namespace Digitalmes.Application.Mes;

/// <summary>
/// 产能报表服务
/// </summary>
[ApiExplorerSettings(GroupName = "mes")]
[AllowAnonymous]
public sealed class ReportProductivityService : IDynamicWebApiService
{
    private readonly SugarRepository<ProdTransSn> _tranSnRepo;

    public ReportProductivityService(SugarRepository<ProdTransSn> tranSnRepo)
    {
        _tranSnRepo = tranSnRepo;
    }

    /// <summary>
    /// 每天产能（线上站）
    /// </summary>
    /// <param name="lineCode">产线编号</param>
    /// <param name="days"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<object> GetDailyAsync(string lineCode = "", int days = 30)
    {
        var db = _tranSnRepo.Context;

        var time = DateTime.Now.Date;
        var itmeArray = Enumerable.Repeat(time, days).Select((t, i) => t.AddDays(i * -1)).Reverse().ToArray();

        var left = db.Reportable(itmeArray).ToQueryable<DateTime>();
        var right = db.Queryable<ProdTransSn>()
            .Where(s => s.CurRoutingId > 0 && s.IsCompelted)
            .WhereIF(!string.IsNullOrEmpty(lineCode), s => s.CurLineCode == lineCode);
        var list = await db.Queryable(left, right, JoinType.Left,
                (x1, x2) => x1.ColumnName.Date == x2.OfflineTime!.Value.Date)
            .GroupBy((x1, x2) => x1.ColumnName)
            .Select((x1, x2) => new
            {
                okCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
                time = x1.ColumnName.ToString("MM/dd"),
            }).ToListAsync();

        var right2 = db.Queryable<ProdTransSnRecord>()
            .Where(s => s.RoutingId > 0 && s.PassResult == PassResultEnum.NG)
            .WhereIF(!string.IsNullOrEmpty(lineCode), s => s.LineCode == lineCode);
        var list2 = await db.Queryable(left, right2, JoinType.Left,
               (x1, x2) => x1.ColumnName.Date == x2.OutboundTime.Date)
           .GroupBy((x1, x2) => x1.ColumnName)
           .Select((x1, x2) => new
           {
               ngCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
               time = x1.ColumnName.ToString("MM/dd"),
           }).ToListAsync();

        List<object> item = new(list.Count);
        foreach (var x in list)
        {
            item.Add(new
            {
                x.time,
                x.okCount,
                list2.First(s => s.time == x.time).ngCount,
            });
        }

        return item;
    }

    /// <summary>
    /// 每小时产能（线上站）
    /// </summary>
    /// <param name="lineCode">产线编号</param>
    /// <param name="hours"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<object> GetHourlyAsync(string lineCode = "", int hours = 24)
    {
        var db = _tranSnRepo.Context;

        var now = DateTime.Now;
        var time = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0);
        var itmeArray = Enumerable.Repeat(time, hours).Select((t, i) => t.AddHours(i * -1)).Reverse().ToArray();

        var left = db.Reportable(itmeArray).ToQueryable<DateTime>();
        var right = db.Queryable<ProdTransSn>()
            .Where(s => s.CurRoutingId > 0 && s.IsCompelted)
            .WhereIF(!string.IsNullOrEmpty(lineCode), s => s.CurLineCode == lineCode);
        var list = await db.Queryable(left, right, JoinType.Left,
                (x1, x2) => x1.ColumnName <= x2.OutboundTime && x2.OutboundTime < x1.ColumnName.AddHours(1))
            .GroupBy((x1, x2) => x1.ColumnName)
            .Select((x1, x2) => new
            {
                okCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
                time = x1.ColumnName.ToString("HH:00"),
            }).ToListAsync();

        var right2 = db.Queryable<ProdTransSnRecord>()
            .Where(s => s.RoutingId > 0 && s.PassResult == PassResultEnum.NG)
            .WhereIF(!string.IsNullOrEmpty(lineCode), s => s.LineCode == lineCode);
        var list2 = await db.Queryable(left, right2, JoinType.Left,
              (x1, x2) => x1.ColumnName <= x2.OutboundTime && x2.OutboundTime < x1.ColumnName.AddHours(1))
          .GroupBy((x1, x2) => x1.ColumnName)
          .Select((x1, x2) => new
          {
              ngCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.Id > 0, 1, 0)),
              time = x1.ColumnName.ToString("HH:00"),
          }).ToListAsync();

        List<object> item = new(list.Count);
        foreach (var x in list)
        {
            item.Add(new
            {
                x.time,
                x.okCount,
                list2.First(s => s.time == x.time).ngCount,
            });
        }

        return item;
    }

    /// <summary>
    /// 生产统计（按产线、工位、天）
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public async Task<object> GetDailyStatAsync(ProductivityParam param)
    {
        var db = _tranSnRepo.Context;

        var time = (param.EndTime ?? DateTime.Now).Date;
        var count = param.StartTime.HasValue ? (time - param.StartTime.Value.Date).Days + 1 : 30; // 默认 30 天
        var itmeArray = Enumerable.Repeat(time, count).Select((t, i) => t.AddDays(i * -1)).Reverse().ToArray();

        var left = db.Reportable(itmeArray).ToQueryable<DateTime>();
        var right = db.Queryable<ProdTransSnRecord>()
            .WhereIF(!string.IsNullOrEmpty(param.LineCode), s => s.LineCode == param.LineCode)
            .WhereIF(!string.IsNullOrEmpty(param.OpCode), s => s.OpCode == param.OpCode);
        var list = await db.Queryable(left, right, JoinType.Left,
                (x1, x2) => x1.ColumnName.Date == x2.OutboundTime.Date)
            .GroupBy((x1, x2) => x1.ColumnName)
            .Select((x1, x2) => new
            {
                okCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.PassResult == PassResultEnum.OK || x2.PassResult == PassResultEnum.MandatoryOK, 1, 0)),
                ngCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.PassResult == PassResultEnum.NG || x2.PassResult == PassResultEnum.MandatoryNG, 1, 0)),
                time = x1.ColumnName.ToString("MM/dd"),
            }).ToListAsync();

        return list;
    }

    /// <summary>
    /// 生产统计（按产线、工位、小时）
    /// </summary>
    /// <returns></returns>
    [HttpGet]
    public async Task<object> GetHourlyStatAsync(ProductivityParam param)
    {
        var db = _tranSnRepo.Context;

        var now = param.EndTime ?? DateTime.Now;
        int hour = param.EndTime.HasValue ? 23 : now.Hour; // 选择日期后，定位到该天的23点
        var time = new DateTime(now.Year, now.Month, now.Day, hour, 0, 0);

        var count = 24; // 默认 24 h
        var itmeArray = Enumerable.Repeat(time, count).Select((t, i) => t.AddHours(i * -1)).Reverse().ToArray();

        var left = db.Reportable(itmeArray).ToQueryable<DateTime>();
        var right = db.Queryable<ProdTransSnRecord>()
            .WhereIF(!string.IsNullOrEmpty(param.LineCode), s => s.LineCode == param.LineCode)
            .WhereIF(!string.IsNullOrEmpty(param.OpCode), s => s.OpCode == param.OpCode);
        var list = await db.Queryable(left, right, JoinType.Left,
                (x1, x2) => x1.ColumnName <= x2.OutboundTime && x2.OutboundTime < x1.ColumnName.AddHours(1))
            .GroupBy((x1, x2) => x1.ColumnName)
            .Select((x1, x2) => new
            {
                okCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.PassResult == PassResultEnum.OK || x2.PassResult == PassResultEnum.MandatoryOK, 1, 0)),
                ngCount = SqlFunc.AggregateSum(SqlFunc.IIF(x2.PassResult == PassResultEnum.NG || x2.PassResult == PassResultEnum.MandatoryNG, 1, 0)),
                time = x1.ColumnName.ToString("HH:00"),
            }).ToListAsync();

        return list;
    }

    /// <summary>
    /// 找到最近完工下线或NG的产品（线上站）
    /// </summary>
    /// <param name="lineCode">产线编号</param>
    /// <param name="seconds"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<List<ProdTransSnDto>> GetRtAsync(string lineCode = "", int seconds = 60)
    {
        var time = DateTime.Now.AddSeconds(seconds * -1);
        var query = await _tranSnRepo.AsQueryable()
            .Where(s => s.CurRoutingId > 0 && s.OutboundTime > time)
            .Where(s => s.IsCompelted || s.Status == SnStatusEnum.NG)
            .WhereIF(!string.IsNullOrEmpty(lineCode), s => s.CurLineCode == lineCode)
            .OrderByDescending(s => s.OutboundTime)
            .ToListAsync();

        return query.Adapt<List<ProdTransSnDto>>();
    }
}
